/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dx.cf.code;

import com.android.dx.util.IntList;
import com.android.dx.util.MutabilityControl;

/**
 * List of (value, target) mappings representing the choices of a
 * {@code tableswitch} or {@code lookupswitch} instruction. It also holds the
 * default target for the switch.
 */
public final class SwitchList extends MutabilityControl {

	/** {@code non-null;} list of test values */
	private final IntList values;

	/**
	 * {@code non-null;} list of targets corresponding to the test values; there
	 * is always one extra element in the target list, to hold the default
	 * target
	 */
	private final IntList targets;

	/** ultimate size of the list */
	private int size;

	/**
	 * Constructs an instance.
	 * 
	 * @param size
	 *            {@code >= 0;} the number of elements to be in the table
	 */
	public SwitchList(int size) {
		super(true);
		this.values = new IntList(size);
		this.targets = new IntList(size + 1);
		this.size = size;
	}

	/** {@inheritDoc} */
	@Override
	public void setImmutable() {
		values.setImmutable();
		targets.setImmutable();
		super.setImmutable();
	}

	/**
	 * Gets the size of the list.
	 * 
	 * @return {@code >= 0;} the list size
	 */
	public int size() {
		return size;
	}

	/**
	 * Gets the indicated test value.
	 * 
	 * @param n
	 *            {@code >= 0;}, &lt; size(); which index
	 * @return the test value
	 */
	public int getValue(int n) {
		return values.get(n);
	}

	/**
	 * Gets the indicated target. Asking for the target at {@code size()}
	 * returns the default target.
	 * 
	 * @param n
	 *            {@code >= 0, <= size();} which index
	 * @return {@code >= 0;} the target
	 */
	public int getTarget(int n) {
		return targets.get(n);
	}

	/**
	 * Gets the default target. This is just a shorthand for
	 * {@code getTarget(size())}.
	 * 
	 * @return {@code >= 0;} the default target
	 */
	public int getDefaultTarget() {
		return targets.get(size);
	}

	/**
	 * Gets the list of all targets. This includes one extra element at the end
	 * of the list, which holds the default target.
	 * 
	 * @return {@code non-null;} the target list
	 */
	public IntList getTargets() {
		return targets;
	}

	/**
	 * Gets the list of all case values.
	 * 
	 * @return {@code non-null;} the case value list
	 */
	public IntList getValues() {
		return values;
	}

	/**
	 * Sets the default target. It is only valid to call this method when all
	 * the non-default elements have been set.
	 * 
	 * @param target
	 *            {@code >= 0;} the absolute (not relative) default target
	 *            address
	 */
	public void setDefaultTarget(int target) {
		throwIfImmutable();

		if (target < 0) {
			throw new IllegalArgumentException("target < 0");
		}

		if (targets.size() != size) {
			throw new RuntimeException("non-default elements not all set");
		}

		targets.add(target);
	}

	/**
	 * Adds the given item.
	 * 
	 * @param value
	 *            the test value
	 * @param target
	 *            {@code >= 0;} the absolute (not relative) target address
	 */
	public void add(int value, int target) {
		throwIfImmutable();

		if (target < 0) {
			throw new IllegalArgumentException("target < 0");
		}

		values.add(value);
		targets.add(target);
	}

	/**
	 * Shrinks this instance if possible, removing test elements that refer to
	 * the default target. This is only valid after the instance is fully
	 * populated, including the default target (naturally).
	 */
	public void removeSuperfluousDefaults() {
		throwIfImmutable();

		int sz = size;

		if (sz != (targets.size() - 1)) {
			throw new IllegalArgumentException("incomplete instance");
		}

		int defaultTarget = targets.get(sz);
		int at = 0;

		for (int i = 0; i < sz; i++) {
			int target = targets.get(i);
			if (target != defaultTarget) {
				if (i != at) {
					targets.set(at, target);
					values.set(at, values.get(i));
				}
				at++;
			}
		}

		if (at != sz) {
			values.shrink(at);
			targets.set(at, defaultTarget);
			targets.shrink(at + 1);
			size = at;
		}
	}
}
